﻿using System;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Core;
using MugenInjection.Exceptions;
using MugenInjection.Interface;
using Should;

namespace MugenInjectionTest.Core
{
    [TestClass]
    public class ChildInjectorTest : TestBase
    {
        [TestMethod]
        public void CreateChildTest()
        {
            using (var injector = GetInjector())
            {
                injector.GetParent().ShouldBeNull();
                injector.GetRoot().ShouldEqual(injector);

                injector.ChildInjectors.ShouldBeEmpty();
                IInjector child = injector.CreateChild();
                injector.ChildInjectors.Count.ShouldEqual(1);
                injector.ChildInjectors.Contains(child).ShouldBeTrue();

                child.GetParent().ShouldEqual(injector);
                child.GetRoot().ShouldEqual(injector);
            }
        }

        [TestMethod]
        public void WhenParentDisposedChildShouldBeDisposedTest()
        {
            using (var injector = GetInjector())
            {
                var child = injector.CreateChild();
                injector.ChildInjectors.Contains(child).ShouldBeTrue();

                child.IsDisposed.ShouldBeFalse();
                injector.IsDisposed.ShouldBeFalse();

                injector.Dispose();
                injector.ChildInjectors.Contains(child).ShouldBeFalse();
                child.IsDisposed.ShouldBeTrue();
                injector.IsDisposed.ShouldBeTrue();
            }
        }

        [TestMethod]
        public void WhenChildDisposedParentShouldRemoveItTest()
        {
            using (var injector = GetInjector())
            {
                var child = injector.CreateChild();
                injector.ChildInjectors.Contains(child).ShouldBeTrue();
                child.Dispose();
                injector.ChildInjectors.Contains(child).ShouldBeFalse();
            }
        }

        [TestMethod]
        public void UseParentToResolveSettingTest()
        {
            const string parentValue = "parentValue";
            const string childValue = "childValue";
            using (var injector = GetInjector())
            {
                injector.Bind<ISomeInterface>().To<SomeClass>();
                injector.Bind<string>().ToConstant(parentValue);

                using (var child = (MugenInjector)injector.CreateChild())
                {
                    //override binding in child container.
                    child.Bind<string>().ToConstant(childValue);

                    //If not find binding, resolve in parent(use parent binding).
                    injector.Settings.UseParentToResolve = true;

                    var parentAlfa = injector.Get<ISomeInterface>();
                    var childAlfa = child.Get<ISomeInterface>();
                    parentAlfa.Value.ShouldEqual(parentValue);
                    childAlfa.Value.ShouldEqual(parentValue);

                    //changing flag
                    child.Settings.UseParentToResolve = false;
                    parentAlfa = injector.Get<ISomeInterface>();
                    childAlfa = child.Get<ISomeInterface>();
                    parentAlfa.Value.ShouldEqual(parentValue);
                    childAlfa.Value.ShouldEqual(childValue);
                }
            }
        }

        [TestMethod]
        public void NotResolveFromChildTest()
        {
            using (var injector = GetInjector())
            {
                var child = (MugenInjector)injector.CreateChild();
                child.Bind<ISomeInterface>().To<SomeClass>();
                TestUtils.Catch<BindingNotFoundException>(() => injector.Get<ISomeInterface>());
            }
        }

        [TestMethod]
        public void SetParentTest()
        {
            using (var injector = GetInjector())
            {
                var child = new MugenInjector();
                child.GetParent().ShouldBeNull();
                child.SetParent(injector);
                child.GetParent().ShouldEqual(injector);
                child.ChildInjectors.ShouldBeEmpty();
                injector.ChildInjectors.Count.ShouldEqual(1);
                injector.ChildInjectors.Contains(child).ShouldBeTrue();

                //Set the same parent twice
                child.SetParent(injector);
                child.GetParent().ShouldEqual(injector);
                child.ChildInjectors.ShouldBeEmpty();
                injector.ChildInjectors.Count.ShouldEqual(1);
                injector.ChildInjectors.Contains(child).ShouldBeTrue();

                TestUtils.Catch<ArgumentException>(() => child.SetParent(new MugenInjector()));

                //Remove parent.
                child.SetParent(null);
                child.GetParent().ShouldBeNull();
                child.ChildInjectors.ShouldBeEmpty();
                injector.ChildInjectors.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void AddChildTest()
        {
            using (var injector = GetInjector())
            {
                var parent = new MugenInjector();
                parent.AddChild(injector);
                injector.GetParent().ShouldEqual(parent);
                parent.ChildInjectors.Count.ShouldEqual(1);
                parent.ChildInjectors.Contains(injector).ShouldBeTrue();
                parent.GetParent().ShouldBeNull();

                //add the same child twice
                parent.AddChild(injector);
                injector.GetParent().ShouldEqual(parent);
                parent.ChildInjectors.Count.ShouldEqual(1);
                parent.ChildInjectors.Contains(injector).ShouldBeTrue();
                parent.GetParent().ShouldBeNull();

                TestUtils.Catch<ArgumentException>(() => new MugenInjector().AddChild(injector));
                TestUtils.Catch<ArgumentException>(() => injector.AddChild(null));
            }
        }

        [TestMethod]
        public void PassParentToConstructorTest()
        {
            using (var injector = GetInjector())
            {
                //Set the parent
                var child = new MugenInjector(injector);
                child.GetParent().ShouldEqual(injector);
                child.ChildInjectors.ShouldBeEmpty();
                injector.ChildInjectors.Count.ShouldEqual(1);
                injector.ChildInjectors.Contains(child).ShouldBeTrue();
            }
        }
    }

    public class SomeClass : ISomeInterface
    {
        public string Value { get; set; }

        public SomeClass(string value)
        {
            Value = value;
        }

    }

    public interface ISomeInterface { string Value { get; set; } }
}